#include "Networking.h"

using namespace std;

DWORD WINAPI ClientThread (LPVOID lpParam)
{
	SOCKET sCl = (SOCKET)lpParam;	// connected client socket
	char szRecvBuff[1024];			// buffer for received messages
	char *szRecvData = 0;			// full sentence from client or more
	int lenRecvBuff = 0;			// received buffer (szRecvBuff) length
	int lenRecvData = 0;			// szRecvData length
	int allocRecvData = 0;			// memory allocated for szRecvData buffer
	int ret;						// send/recv-function return value
	int idx = 0;					// index for checked part of szRecvData

//	std::cout << "Client Thread Started" << endl;

	for(int i = 0; i < 1; i++) {
		// Receive data from client
		ret = recv(sCl, szRecvBuff, 1024, 0);	// [ret-2] == 13 & [ret-1] == 10; dont know why did they appear there
		if (szRecvBuff[ret-1] == 10 || szRecvBuff[ret-1] == 13 || szRecvBuff[ret-1] == ' ') {
			szRecvBuff[ret-1] = 0;
			ret--;
			if (szRecvBuff[ret-2] == 10 || szRecvBuff[ret-2] == 13 || szRecvBuff[ret-2] == ' ') {
				szRecvBuff[ret-2] = 0;
				ret--;
			}
		}
		else szRecvBuff[ret] = 0;
		if (ret == 0) { // No data received
			break;
		}
		else {
			if (ret == SOCKET_ERROR) { // recv cause a socket error
				std::cout << "Receive data failed" << endl;
				break;
			}
		}

		lenRecvBuff = (int)strlen(szRecvBuff);

		if(lenRecvData + lenRecvBuff > allocRecvData) {		// Make more space for szRecvData buffer
			void *pNewBuff = realloc(szRecvData, lenRecvData + lenRecvBuff);
			if(pNewBuff == 0) {								// There are no free memory
				std::cerr << "Error: no memory for buffers.\n";
				break;
			}
			else {
				allocRecvData = lenRecvData + lenRecvBuff;
				szRecvData = (char *)pNewBuff;
			}
		}

		// Stick data from szRecvData and szRecvBuff
		memcpy(szRecvData + lenRecvData, szRecvBuff, lenRecvBuff);
		lenRecvData += lenRecvBuff;

		szRecvData[lenRecvData] = 0;

		for(idx; idx < lenRecvData; idx++) {
			if(szRecvData[idx] == ';') {
				char *szSendBuff = (char *)malloc(idx + 1);
				memcpy(szSendBuff, szRecvData, idx);
				szSendBuff[idx] = 0;
				std::cout << "Current Query: " << szSendBuff << endl;
				
				// send szSendBuff for lexical analysis
				cText sentence;
				sentence.setMsg(szSendBuff);

				cToken token;

				std::string dbResult;
				cTree tree;
				try {
					tree.readTree(&sentence, &token);
					dbResult = tree.execTree();
				} catch (dbmsException e) {
					dbResult = e.getMessage();
				}

				ret = send(sCl, (char *) dbResult.data(), strlen(dbResult.data()), 0);

				if (ret == SOCKET_ERROR) {
					std::cout << "Send data failed" << endl;
//					break;
				}

				free(szSendBuff);
				memmove(szRecvData, szRecvData + idx + 1, lenRecvData - idx - 1);
				lenRecvData -= (idx + 1);
				idx = 0;

			}
			else {
				if (idx == lenRecvData - 1) {
					std::cout << "Current Query: " << szRecvData << endl;

					// send szSendBuff for lexical analysis
					cText sentence;
					sentence.setMsg(szRecvData);

					cToken token;

					std::string dbResult;
					cTree tree;
					try {
						tree.readTree(&sentence, &token);
						dbResult = tree.execTree();
					} catch (dbmsException e) {
						dbResult = e.getMessage();
					}


					ret = send(sCl, (char *) dbResult.data(), strlen(dbResult.data()), 0);

					if (ret == SOCKET_ERROR) {
						std::cout << "Send data failed" << endl;
					}
				}
			}
		}

	}

	closesocket(sCl);
//	std::cout << "Client Thread Finished" << endl;

	return 0;
}

DWORD WINAPI ServerThread (LPVOID lpParam)
{
	SOCKET sListen, // socket binded to the port
		sClient; // new client socket
	struct sockaddr_in localaddr, // server address
		clientaddr; // client address
	HANDLE hThread; // connected client thread handler
	DWORD dwThreadId; // client thread identifier
	int iSize; // address size
	std::cout <<"*************************************************************" <<endl;
	std::cout <<"*********** D B M S  L I G H T  S E R V E R  V 1.0 **********" <<endl;
	std::cout <<"*************************************************************" <<endl;
	std::cout <<"** VENDORS: HCI NSU, Nikolay A.Makarov,Evgeniy I.Kuznetcov **" <<endl;
	std::cout <<"** WEB SITE: http://code.google.com/p/dbmslight/ ************" <<endl;
	std::cout <<"*************************************************************" <<endl;
	std::cout << "Server Started..." << endl;

	// Create socket, which will listen to the port
	sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sListen == SOCKET_ERROR) {
		std::cout << "Can't create socket sListen" << endl;
	}

	// Fill in local address
	localaddr.sin_family = AF_INET;
	localaddr.sin_port = htons(65001);
	localaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	// Bind socket with address
	if(bind(sListen, (struct sockaddr*)&localaddr, sizeof(localaddr)) == SOCKET_ERROR) {
		std::cout << "Can't bind socket" << endl;
		return -1;
	}

//	std::cout << "Socket binded successfully" << endl;

	// Start listen to the port
	if(listen(sListen, 3) == SOCKET_ERROR) {
		std::cout << "Can't start listen" << endl;
		return -1;
	}

//	std::cout << "Listening started" << endl;

	iSize = sizeof(clientaddr);

	for(;;) {
		// Accept connection from queue or wait for client
		sClient = accept(sListen, (struct sockaddr *)&clientaddr, &iSize);
		if (sClient ==	INVALID_SOCKET) {
			std::cout << "Accept failed" << endl;
			break;
		}

		// Client message exchange thread
		hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)sClient, 0, &dwThreadId);
		if (hThread == NULL) {
			std::cout << "Can't create ClientThread" << endl;
			break;
		}

		CloseHandle(hThread);
	}
	// Close socket after thread work
	closesocket(sListen);
	
	std::cout << "Server Thread Finished" << endl;

	return 0;
}
